﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Configuration;

namespace FNN.Genetic
{
    public class Crossbreeding
    {
        private Random _randomizer = new Random();

        protected DnaHelix DominantDnaHelix;
        protected DnaHelix RecessiveDnaHelix;

        /// <summary>
        ///     Доминантная ДНК
        /// </summary>
        public DnaHelix DominantDna
        {
            get { return DominantDnaHelix; }
            set { DominantDnaHelix = value; }
        }

        /// <summary>
        ///     Рецессивная ДНК
        /// </summary>
        public DnaHelix RecessiveDna
        {
            get { return RecessiveDnaHelix; }
            set { RecessiveDnaHelix = value; }
        }

        /// <summary>
        ///     Запустить скрещивание
        /// </summary>
        /// <returns>Результирующая ДНК</returns>
        public DnaHelix Perform()
        {
            double dominantShare = Convert.ToDouble(ConfigurationManager.AppSettings["dominantDnaShare"]) / 100;
            double recessiveShare = 1.0 - dominantShare;

            var dominantStructure = DominantDnaHelix.GetNetworkStructure();
            var recessiveStructure = RecessiveDnaHelix.GetNetworkStructure();

            int dominantLayersCount = Convert.ToInt32(Math.Round(dominantStructure.LayerStructures.Length * dominantShare));
            int recessiveLayersCount = Convert.ToInt32(Math.Round(recessiveStructure.LayerStructures.Length * recessiveShare));
            int dominantSynapsesCount = Convert.ToInt32(Math.Round(dominantStructure.SynapseStructures.Length * dominantShare));
            int recessiveSynapsesCount = Convert.ToInt32(Math.Round(recessiveStructure.SynapseStructures.Length * recessiveShare));

            var resultStructure = new NetworkStructure();
            var resultLayersStructures = new LayerStructure[dominantLayersCount + recessiveLayersCount].ToList();
            var resultSynapsesStructures = new List<SynapseStructure>();

            for (int i = 0; i < dominantLayersCount; i++)
            {
                var targetIndex = _randomizer.Next(0, dominantStructure.LayerStructures.Length);
                int newElementIndex = _randomizer.Next(0, dominantLayersCount + recessiveLayersCount);
                while (resultLayersStructures.ElementAtOrDefault(newElementIndex) != null)
                {
                    newElementIndex = _randomizer.Next(0, dominantLayersCount + recessiveLayersCount);
                }
                string newLayerChain = String.Join(";", dominantStructure.LayerStructures[targetIndex].FunctionsList);
                var newStructure = new LayerStructure(newLayerChain);
                resultLayersStructures[newElementIndex] = newStructure;
            }

            for (int i = 0; i < recessiveLayersCount; i++)
            {
                var targetIndex = _randomizer.Next(0, recessiveStructure.LayerStructures.Length);
                int newElementIndex = _randomizer.Next(0, dominantLayersCount + recessiveLayersCount);
                while (resultLayersStructures.ElementAtOrDefault(newElementIndex) != null)
                {
                    newElementIndex = _randomizer.Next(0, dominantLayersCount + recessiveLayersCount);
                }
                string newLayerChain = String.Join(";", recessiveStructure.LayerStructures[targetIndex].FunctionsList);
                var newStructure = new LayerStructure(newLayerChain);
                resultLayersStructures[newElementIndex] = newStructure;
            }

            resultStructure.LayerStructures = resultLayersStructures.ToArray();

            for (int i = 0; i < dominantSynapsesCount; i++)
            {
                var targetIndex = _randomizer.Next(0, dominantSynapsesCount);
                int inputNeuronId = dominantStructure.SynapseStructures[targetIndex].InputNeuronId;
                int outputNeuronId = dominantStructure.SynapseStructures[targetIndex].OutputNeuronId;

                string newSynapseChain = inputNeuronId.ToString() + ";" + outputNeuronId.ToString();
                var newStructure = new SynapseStructure(newSynapseChain);

                bool inputNeuronExists = (((inputNeuronId / 1000) <= resultLayersStructures.Count) && ((inputNeuronId % 1000) < resultLayersStructures[inputNeuronId / 1000].FunctionsList.Length));
                bool outputNeuronExists = (((outputNeuronId / 1000) <= resultLayersStructures.Count) && ((outputNeuronId % 1000) < resultLayersStructures[outputNeuronId / 1000].FunctionsList.Length));

                if (!inputNeuronExists)
                {
                    var targetLayerIndex = _randomizer.Next(0, resultLayersStructures.Count);
                    newStructure.InputNeuronId = 1000 * (targetLayerIndex + 1) + _randomizer.Next(0, resultLayersStructures[targetLayerIndex].FunctionsList.Length);
                }

                if (!outputNeuronExists)
                {
                    var targetLayerIndex = _randomizer.Next(0, resultLayersStructures.Count);
                    newStructure.OutputNeuronId = 1000 * (targetLayerIndex + 1) + _randomizer.Next(0, resultLayersStructures[targetLayerIndex].FunctionsList.Length);
                }
                
                resultSynapsesStructures.Add(newStructure);
            }

            for (int i = 0; i < recessiveSynapsesCount; i++)
            {
                var targetIndex = _randomizer.Next(0, recessiveSynapsesCount);
                int inputNeuronId = recessiveStructure.SynapseStructures[targetIndex].InputNeuronId;
                int outputNeuronId = recessiveStructure.SynapseStructures[targetIndex].OutputNeuronId;

                string newSynapseChain = inputNeuronId.ToString() + ";" + outputNeuronId.ToString();
                var newStructure = new SynapseStructure(newSynapseChain);

                bool inputNeuronExists = (((inputNeuronId / 1000) <= resultLayersStructures.Count) && ((inputNeuronId % 1000) < resultLayersStructures[inputNeuronId / 1000].FunctionsList.Length));
                bool outputNeuronExists = (((outputNeuronId / 1000) <= resultLayersStructures.Count) && ((outputNeuronId % 1000) < resultLayersStructures[outputNeuronId / 1000].FunctionsList.Length));

                if (!inputNeuronExists)
                {
                    var targetLayerIndex = _randomizer.Next(0, resultLayersStructures.Count);
                    newStructure.InputNeuronId = 1000 * (targetLayerIndex + 1) + _randomizer.Next(0, resultLayersStructures[targetLayerIndex].FunctionsList.Length);
                }

                if (!outputNeuronExists)
                {
                    var targetLayerIndex = _randomizer.Next(0, resultLayersStructures.Count);
                    newStructure.OutputNeuronId = 1000 * (targetLayerIndex + 1) + _randomizer.Next(0, resultLayersStructures[targetLayerIndex].FunctionsList.Length);
                }

                resultSynapsesStructures.Add(newStructure);
            }

            resultStructure.SynapseStructures = resultSynapsesStructures.ToArray();            
            return new DnaHelix(resultStructure);
        }


        /// <summary>
        ///     Запустить скрещивание с параметрами
        /// </summary>
        /// <param name="dominantHelix">Доминантная ДНК</param>
        /// <param name="recessiveHelix">Рецессивная ДНК</param>
        /// <returns>Результирующая ДНК</returns>
        public DnaHelix Perform(DnaHelix dominantHelix, DnaHelix recessiveHelix)
        {
            DominantDnaHelix = dominantHelix;
            RecessiveDnaHelix = recessiveHelix;
            return Perform();
        }
    }
}
